package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 报警规则Service业务层处理
 *
 * @author xs
 * @date 2025-10-28
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Value("${alertRule.roleRepair}")
    private String roleRepair;
    @Value("${alertRule.roleAdmin}")
    private String roleAdmin;
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private IAlertDataService alertDataService;


    @Override
    @Transactional
    public void alertRuleFilter() {

        //1. 查询报警规则表中的所有报警规则
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (count <= 0) {
            log.error("[定时报警规则校验]没有任何报警规则，无需处理");
            return;
        }
        //2. 查询Redis中所有设备上报的数据
        List<Object> values = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (CollUtil.isEmpty(values)) {
            log.error("[定时报警规则校验]没有任何上报数据，无需处理");
            return;
        }
        //3. 将查询到的设备上报数据进行格式化处理
        List<DeviceData> deviceDataList = new ArrayList<>();
        values.forEach(v -> {
            List<DeviceData> deviceData = JSONUtil.toList((String) v, DeviceData.class);
            deviceDataList.addAll(deviceData);
        });
        //4. 获取到全部的设备数据，进行过滤处理
        for (DeviceData deviceData : deviceDataList) {
            //4.1 校验设备上报时间是否太久（由业务规则决定，可变）
            long limit = 600;//时间超过10分钟的报警数据不在处理
            long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
            if (between > limit) {
                log.error("[定时报警规则校验]报警数据超过10分钟,不在处理");
                continue;
            }
            //4.2 查询上报数据对应的设备绑定的全部报警规则数据
            //4.2.1 查询当前上报数据对应的产品类型下同样的功能绑定的全部规则数据
            List<AlertRule> productAlertRuleList = this.lambdaQuery().eq(AlertRule::getStatus, 1)
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, -1).list();

            //4.2.2 查询当前上报数据对应的设备下绑定的规则数据
            List<AlertRule> deviceAlertRuleList = this.lambdaQuery().eq(AlertRule::getStatus, 1)
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId()).list();
            //4.2.3 合并所有报警规则
            productAlertRuleList.addAll(deviceAlertRuleList);
            //4.2.4 判断规则是否为空
            if (CollUtil.isEmpty(productAlertRuleList)) {
                log.error("[定时报警规则校验]报警规则为空");
                continue;
            }
            //4.2.5 对规则去重，得到全部规则列表
            ArrayList<AlertRule> allAlertRuleList = CollUtil.distinct(productAlertRuleList);
            //第一层校验：判断生效时间是否合适
            for (AlertRule alertRule : allAlertRuleList) {
                //比较设备数据上报时间是否在生效时间段内
                String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
                String[] strings = alertEffectivePeriod.split("~");

                LocalTime startTime = LocalTime.parse(strings[0]);
                LocalTime endTime = LocalTime.parse(strings[1]);
                LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();

                if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
                    log.error("[定时报警规则校验]不在生效时间内,无需处理");
                    continue;
                }
                //第二层校验：判断数据阈值
                Double dateValue = Double.valueOf(deviceData.getDataValue());//设备上报数据
                Double value = Double.valueOf(alertRule.getValue());//阈值
                String operator = alertRule.getOperator();//比较运算符
                //x大于y，结果就是1； x等于y，结果就是0； x小于y，结果就是-1
                int compare = NumberUtil.compare(dateValue, value);
                //达到阈值
                if (operator.equals(">=") && compare >= 0 || operator.equals("<") && compare < 0) {
                    //第三层校验：检查沉默周期是否存在，存在就直接结束
                    String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                    String silentVal = stringRedisTemplate.opsForValue().get(silentKey);
                    if (StrUtil.isNotBlank(silentVal)) {
                        log.error("[定时报警规则校验]沉默时间未结束,无需处理");
                        continue;
                    } else {
                        //第四层校验：判断持续周期对应的次数是否达到（没达到则直接结束）
                        String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                        Long increment = stringRedisTemplate.opsForValue().increment(countKey);
                        if (increment < alertRule.getDuration()) {
                            log.error("[定时报警规则校验]持续周期还未达到,无需处理");
                            continue;
                        }
                        //保存沉默周期的值到Redis，过期时间为沉默周期对应的秒数
                        stringRedisTemplate.opsForValue().setIfAbsent(silentKey, "123", alertRule.getAlertSilentPeriod() * 60, TimeUnit.SECONDS);
                        //删除Redis中持续抽周期缓存数据
                        stringRedisTemplate.delete(countKey);

                        //查询老人的护理人员
                        List<Long> nursingIds = null;
                        //如果是老人异常数据，则查询对应护理人员
                        if (alertRule.getAlertDataType() == 0) {
                            //如果设备是移动设备，则直接查到老人对应的护理人员
                            if (deviceData.getLocationType() == 0) {
                                nursingIds = deviceMapper.selectNuringIdsByMoveIotId(deviceData.getIotId());
                            } else {
                                //如果设备是固定设备，则需要关联床位查询老人对应的护理人员
                                nursingIds = deviceMapper.selectNuringIdsByStopIotId(deviceData.getIotId());
                            }
                        } else {
                            //如果是设备异常数据，则查询维修工
                            nursingIds = sysUserRoleMapper.selectUserIdsByRoleName(roleRepair);
                        }
                        //查询超级管理员
                        List<Long> adminIds = sysUserRoleMapper.selectUserIdsByRoleName(roleAdmin);
                        //合并用户ID
                        nursingIds.addAll(adminIds);
                        //去重用户ID （等待查看异常报警的所有用户ID）
                        List<Long> nursingIdList = CollUtil.distinct(nursingIds);
                        //封装报警基础数据对象
                        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
                        alertData.setType(alertRule.getAlertDataType());
                        alertData.setAlertRuleId(alertRule.getId());
                        alertData.setId(null);
                        alertData.setStatus(0);
                        String reasonTemplate = "功能{}{}{}，持续了{}个周期才报警，沉默周期：{}";
                        String alertReason = CharSequenceUtil.format(reasonTemplate,
                                alertRule.getFunctionId(),
                                alertRule.getOperator(),
                                alertRule.getValue(),
                                alertRule.getDuration(),
                                alertRule.getAlertSilentPeriod()
                        );
                        alertData.setAlertReason(alertReason);

                        List<AlertData> alertDataList = new ArrayList<>();
                        for (Long id : nursingIdList) {
                            AlertData userAlertData = BeanUtil.toBean(alertData, AlertData.class);
                            userAlertData.setUserId(id);
                            userAlertData.setIotId(deviceData.getIotId());
                            alertDataList.add(userAlertData);
                        }
                        boolean saveBatch = alertDataService.saveBatch(alertDataList);
                        if (!saveBatch){
                          throw new RuntimeException("保存用户保存数据失败");
                        }
                    }
                }else {
                    log.error("[定时报警规则校验]数据上报的值没有达到规定阈值无需处理");
                    continue;
                }
            }

        }
    }
}
